<template>
  <el-form-item
    v-for="item in items"
    :key="item.prop"
    :label="item.label"
    :prop="item.prop"
    v-bind="item.formItemProps"
  >
    <template v-if="formLabelIsVNode(item)" #label>
      <component :is="label" />
    </template>

    <component
      :is="renderComponent(item)"
      ref="componentRef"
      v-bind="item.componentProps"
      v-model="formValue[item.prop]"
      @change="(value) => handleChange(value, item)"
    />
  </el-form-item>
</template>

<script setup>
import { isString } from '@/utils/type'
import { componentMap } from './component'

const modelValue = defineModel({ type: Object, default: () => ({}) })
const emit = defineEmits(['change'])
defineProps({
  items: {
    type: Array,
    default: () => [],
  },
})

const componentRef = ref(null)

const formLabelIsVNode = computed(() => (item) => !isString(item.label))
const formValue = computed({
  get: () => modelValue.value,
  set: (value) => (modelValue.value = value),
})

// 渲染form-item对应组件
function renderComponent(item) {
  if (item.render) return item.render

  return componentMap.get(item.component)
}

function handleChange(value, item) {
  modelValue.value[item.prop] = value
  emit('change', { value, item, formData: modelValue.value })
}

defineExpose(
  new Proxy(
    {},
    {
      get(target, key) {
        return componentRef.value?.[key]
      },
      has(target, key) {
        return key in componentRef.value
      },
    },
  ),
)
</script>
